package com.qfmy.service.impl.user;

import com.qfmy.mapper.user.UserInfoMapper;
import com.qfmy.service.common.SmsService;
import com.qfmy.service.user.UserInfoService;
import com.qfmy.vo.user.UserInfoVo;
import common.constant.RedisConstant;
import common.exception.BaseException;
import common.login.LoginUser;
import common.login.LoginUserHolder;
import common.result.ResultCodeEnum;
import common.utils.CodeUtils;
import common.utils.JwtUtil;
import common.utils.PasswordUtil;
import common.utils.QrCodeUtils;
import jakarta.validation.constraints.NotEmpty;
import lombok.extern.slf4j.Slf4j;
import model.entity.user.UserInfo;
import model.enums.user.UserStatus;
import model.query.LoginQuery;
import model.query.UserInfoQuery;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * @author 清风明月
 * @Package com.qfmy.service.impl.user
 * @date 2025/5/17
 * @description 用户
 */
@Slf4j
@Service
@SuppressWarnings("all")
public class UserInfoServiceImpl implements UserInfoService {

    private static final int MAX_RETRY_COUNT = 3; // 最大重试次数
    private static final long RETRY_TIME_WINDOW_SEC = 15; // 重试时间窗口（秒）
    /**
     * 注入用户头像
     */
    @Value("${default.avatar}")
    private String userAvatar;

    /**
     * 注入mapper
     */
    @Autowired
    private UserInfoMapper userInfoMapper;
    /**
     * 注入redisTemplate
     */
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 注入ssm服务
     */
    @Autowired
    private SmsService ssmService;

    /**
     * 获取短信验证码
     */
    @Override
    public void getSmsCode(String phone) {
        //创建redis的key
        String key = "sms_code_" + phone;
        //判断redis是否存在
        Boolean result = redisTemplate.hasKey(key);
        //如果存在，抛出异常
        if(result)
        {
            //计算剩余时间
            long expire = redisTemplate.getExpire(key, TimeUnit.SECONDS);
            if(expire > 0)
            {
                throw new BaseException(ResultCodeEnum.APP_SEND_SMS_TOO_OFTEN.getCode(),ResultCodeEnum.APP_SEND_SMS_TOO_OFTEN.getMessage() + "，剩余时间：" + expire + "秒");
            }
        }else
        {
            try {
                //生成验证码
                String code = CodeUtils.getCode(6);
                //发送短信验证码
                ssmService.sendCode(phone,code);
                //将验证码存入redis
                redisTemplate.opsForValue().set(key,code, RedisConstant.APP_LOGIN_CODE_RESEND_TIME_SEC, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error("发送短信验证码失败：{}",e.getMessage());
                throw new BaseException(ResultCodeEnum.SERVICE_ERROR);
            }
        }
    }

    /**
     * 登入
     * @param loginQuery
     * @return
     */
    @Override
    public String login(@NotNull LoginQuery loginQuery) {
       //校验手机号
       UserInfo userInfo = VerifyPhone(loginQuery.getPhone());
       //校验密码
       VerifyPassword(loginQuery,userInfo);
       //校验用户
       VerifyUser(userInfo);
       //创建token
       String token = CreateToken(userInfo);
       return token;
    }

    /**
     * 注册
     * @param loginQuery
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(@NotNull LoginQuery loginQuery) {
        //校验验证码
        VerifyCode(loginQuery.getPhone(),loginQuery.getVerifyCode());
        //判断用户是否存在
        UserInfo userInfo = userInfoMapper.selectCountByPhone(loginQuery.getPhone());
        if(userInfo != null)
        {
            throw new BaseException(ResultCodeEnum.APP_LOGIN_IS_REGISTERED);
        }
        //创建用户
        userInfo = new UserInfo();
        userInfo.setPhone(loginQuery.getPhone());
        //设置默认的用户名称
        String username = "用户" + loginQuery.getPhone().substring(7);
        userInfo.setUsername(username);
        //对密码进行加密
        String password = PasswordUtil.encodePassword(loginQuery.getPassword());
        userInfo.setPassword(password);
        userInfo.setAvatar(userAvatar);
        userInfoMapper.insert(userInfo);
    }

    /**
     * 找回密码
     * @param loginQuery
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void findPassword(@NotNull LoginQuery loginQuery) {
        //校验验证码
        VerifyCode(loginQuery.getPhone(),loginQuery.getVerifyCode());
        //校验用户是否存在
        UserInfo userInfo = userInfoMapper.selectCountByPhone(loginQuery.getPhone());
        if(userInfo != null)
        {
            throw new BaseException(ResultCodeEnum.APP_LOGIN_IS_REGISTERED);
        }
        //更新密码
        String password = PasswordUtil.encodePassword(loginQuery.getPassword());
        userInfoMapper.updatePassword(password,loginQuery.getPhone());
    }

    /**
     * 获取用户信息
     * @return
     */
    @Override
    public UserInfoVo getUserInfo() {
        //获取当前用户手机号
        String phone = getPhone();
        //根据手机号查询用户信息
        UserInfo userInfo = userInfoMapper.selectCountByPhone(phone);
        //拷贝数据
        UserInfoVo userInfoVo = new UserInfoVo();
        userInfoVo.setUserId(userInfo.getUserId());
        userInfoVo.setPhone(userInfo.getPhone());
        userInfoVo.setUsername(userInfo.getUsername());
        userInfoVo.setAvatar(userInfo.getAvatar());
        userInfoVo.setBalance(userInfo.getBalance());
        userInfoVo.setReadTime(userInfo.getReadTime());
        return userInfoVo;
    }

    /**
     * 更新用户信息
     * @param userInfoQuery
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserInfo(@NotNull UserInfoQuery userInfoQuery) {
        //获取当前用户手机号
        String phone = getPhone();
        //根据手机号查询用户信息
        UserInfo userInfo = userInfoMapper.selectCountByPhone(phone);
        //校验密码
        String oldPassword = userInfoQuery.getOldPassword();
        String newPassword = userInfoQuery.getNewPassword();
        Boolean result = PasswordUtil.matches(oldPassword,userInfo.getPassword());
        if(!result)
        {
            throw new BaseException(ResultCodeEnum.APP_LOGIN_PASSWORD_NOT_TRUE);
        }
        //对密码进行加密
        String password = PasswordUtil.encodePassword(newPassword);
        //设置密码
        userInfo.setPassword(password);
        //设置用户名
        userInfo.setUsername(userInfoQuery.getUsername());
        //更新用户信息
        userInfoMapper.updateUserInfo(userInfo);
    }

    /**
     * 更新用户头像
     * @param avatar
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserAvatar(String avatar) {
        //获取当前用户手机号
        String phone = getPhone();
        //更新用户头像
        userInfoMapper.updateUserAvatar(avatar,phone);
    }

    /**
     * 获取用户二维码
     * @return
     */
    @Override
    public String getUserQrCode() {
        //获取用户手机号
        String phone = getPhone();
        //查询用户信息
        UserInfo userInfo = userInfoMapper.selectCountByPhone(phone);
        //创建redis的key
        String key = "user_qr_code_" + userInfo.getPhone();
        //获取redis中的二维码
        String qrCode = redisTemplate.opsForValue().get(key);
        //判断是否存在
        if(qrCode == null)
        {
            // 构建二维码内容，这里可以包含用户信息
            String qrCodeContent = "用户ID:" + userInfo.getUserId() + ",用户名:" + userInfo.getUsername();
            //创建二维码
            String code = null;
            try {
                code = QrCodeUtils.generateQrCode(qrCodeContent);
            } catch (Exception e) {
                log.error("生成二维码失败",e);
                throw new BaseException(ResultCodeEnum. SERVICE_ERROR);
            }
            //保存二维码到redis并且设置过期时间为10小时
            redisTemplate.opsForValue().set(key, code, RedisConstant.APP_QRCODE_TTL, TimeUnit.HOURS);
        }
        return qrCode;
    }

    /**
     * 设置喜欢的小说类型
     * @param name
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setLikeNovelType(String name) {
        //获取当前用户id
        Long userId = LoginUserHolder.getLoginUser().getUserId();
        userInfoMapper.updateLikeNovelType(name,userId);
    }

    /**
     * 保存用户阅读时间
     * @param time
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveUserReadTime(Long time) {
        //获取当前用户id
        Long userId = LoginUserHolder.getLoginUser().getUserId();
        userInfoMapper.updateUserReadTime(time,userId);
    }

    /**
     *
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private String getPhone() {
        //获取当前用户手机号
        LoginUser loginUser = LoginUserHolder.getLoginUser();
        if(loginUser == null)
        {
            throw new BaseException(ResultCodeEnum.APP_LOGIN_AUTH);
        }
        return loginUser.getPhone();
    }

    /**
     * 校验验证码
     * @param phone
     * @param verifyCode
     */
    private void VerifyCode(@NotEmpty(message = "手机号不能为空") String phone, String verifyCode) {
        //创建redis的key
        String key = "sms_code_" + phone;
        //获取redis中的验证码
        String code = redisTemplate.opsForValue().get(key);
        //判断是否存在
        if(code == null)
        {
              throw new BaseException(ResultCodeEnum.APP_LOGIN_CODE_EXPIRED);
        }
        //判断验证码是否正确
        if(!code.equals(verifyCode))
        {
            throw new BaseException(ResultCodeEnum.APP_LOGIN_CODE_ERROR);
        }
    }

    /**
     * 创建token
     * @param userInfo
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private String CreateToken(@NotNull UserInfo userInfo) {
        //创建redis的key
        String key = "token_" + userInfo.getPhone();
        //创建token
        String token = JwtUtil.createToken(userInfo.getPhone(),userInfo.getUsername(),userInfo.getUserId());
        //将token存入redis
        redisTemplate.opsForValue().set(key,token,RedisConstant.APP_LOGIN_TOKEN_TTL_SEC,TimeUnit.HOURS);
        return token;
    }

    /**
     * 校验用户
     * @param userInfo
     */
    private void VerifyUser(@NotNull UserInfo userInfo) {
        //获取用户的状态
        if(!userInfo.getStatus().equals(UserStatus.NORMAL.getCode()))
        {
            throw new BaseException(ResultCodeEnum.APP_ACCOUNT_DISABLED_ERROR);
        }
    }

    /**
     * 校验密码
     */
    /**
     * 校验密码（包含重试机制）
     * @param loginQuery 登录请求参数
     * @param userInfo 用户信息
     */
    private void VerifyPassword(@NotNull LoginQuery loginQuery, @NotNull UserInfo userInfo) {
        String phone = loginQuery.getPhone();
        String retryCounterKey = "retry_count_" + phone;

        // 获取当前的重试计数（使用 INCR 命令原子性增加计数）
        Long retryCount = redisTemplate.opsForValue().increment(retryCounterKey, 1);
        if (retryCount == null) {
            retryCount = 1L;
        }

        // 设置重试计数的过期时间
        redisTemplate.expire(retryCounterKey, RETRY_TIME_WINDOW_SEC, TimeUnit.SECONDS);

        //判断重试是否超过限制
        if (retryCount > MAX_RETRY_COUNT) {
            throw new BaseException(ResultCodeEnum.ACCOUNT_LOCKED);
        }

        // 校验密码
        Boolean isPasswordCorrect = PasswordUtil.matches(loginQuery.getPassword(), userInfo.getPassword());
        if (isPasswordCorrect) {
            // 如果密码正确，清除重试计数
            redisTemplate.delete(retryCounterKey);
        } else {
            // 如果密码错误，抛出异常并提示剩余重试次数
            int remainingTries = MAX_RETRY_COUNT - retryCount.intValue();
            throw new BaseException(ResultCodeEnum.APP_LOGIN_PASSWORD_NOT_TRUE.getCode(),
                    ResultCodeEnum.APP_LOGIN_PASSWORD_NOT_TRUE.getMessage() + "，剩余次数：" + remainingTries + "次");
        }
    }


    /**
     * 校验手机号
     * @param phone
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private UserInfo VerifyPhone(@NotEmpty(message = "手机号不能为空") String phone) {
        //查询用户
        UserInfo userInfo = userInfoMapper.selectCountByPhone(phone);
        if(userInfo == null)
        {
            throw new BaseException(ResultCodeEnum.APP_LOGIN_IS_NOT_REGISTERED);
        }
        return userInfo;
    }


}
